package cn.cnnic.uploadkit;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

import static cn.cnnic.uploadkit.handler.FormHandlerFactory.*;

public class MultipartFormdataUtil {

	public static Map<String, Object> resolveRequestData(InputStream in, String contentType) {
		ByteArrayOutputStream otherout = new ByteArrayOutputStream();

		byte[] buffer = new byte[1024];
		int i = 0;

		try {
			while ((i = in.read(buffer)) != -1) {
				otherout.write(buffer, 0, i);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return resolveRequestData(otherout.toByteArray(), contentType.substring(contentType.indexOf("boundary=") + "boundary=".length()));
	}

	public static Map<String, Object> resolveRequestData(byte[] data, String boundary) {
		Map<String, Object> parameters = new HashMap<String, Object>();
		
		StringBuilder buffer = new StringBuilder();
		String line = null;
		
		int index = 0;
		
		while (index < data.length) {
			if (line == null) {
				index = readLine(data, buffer, index);
				line = clearBuffer(buffer);
			}

			if (isEnd(line, boundary)) {
				break;
			} else if (isNewSeparator(line, boundary)) {
				index = readLine(data, buffer, index);
				line = clearBuffer(buffer);

				Map<String, String> contentParmeter = getContentParameter(line);

				if (contentParmeter.containsKey("filename")) {
					index = getHandler("file").get(index, buffer, data, boundary, contentParmeter, parameters);
				} else {
					index = getHandler("text").get(index, buffer, data, boundary, contentParmeter, parameters);
				}
				line = clearBuffer(buffer);
			} else {
				line = null;
				continue;
			}
		}
		return parameters;
	}

	public static String getContentType(String contentTypeStr) {
		return contentTypeStr.substring(contentTypeStr.indexOf(":") + 1).trim();
	}

	public static Map<String, String> getContentParameter(String line) {
		Map<String, String> result = new HashMap<String, String>();
		String[] params = line.split("; ");

		for (String param : params) {
			if (param.indexOf("Content-Disposition") == -1) {
				String[] keyValue = param.split("=");

				result.put(keyValue[0], keyValue[1].trim().replace("\"", ""));
			} else {
				result.put("ContentDisposition", param.substring(param.indexOf(":") + 1).trim());
			}
		}
		return result;
	}

	public static String clearBuffer(StringBuilder buffer) {
		String result = buffer.toString();
		buffer.delete(0, buffer.length());
		return result;
	}

	public static int readLine(byte[] data, StringBuilder buffer, int index) {
		return readLine(data, buffer, index, true);
	}

	public static int readLine(byte[] data, StringBuilder buffer, int index, boolean isRemoveSuffixBlankLine) {
		int startIndex = index;
		int count = 0;

		while (index < data.length && ((char) data[index]) != '\r' && ((char) data[index]) != '\n') {
			index++;
			count++;
		}
		try {
			buffer.append(new String(data, startIndex, count, "utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		if (isRemoveSuffixBlankLine) {
			index = removeSuffixBlankLine(data, index);
		}

		return index;
	}

	public static int removeSuffixBlankLine(byte[] data, int index) {
		while (index < data.length && ((char) data[index] == '\r' || (char) data[index] == '\n')) {
			index++;
		}
		return index;
	}

	public static boolean isNewSeparator(String line, String boundary) {
		if (line.indexOf(boundary) == -1) {
			return false;
		} else {
			return true;
		}
	}

	public static boolean isEnd(String line, String boundary) {
		if (line.indexOf(boundary + "--") == -1) {
			return false;
		} else {
			return true;
		}
	}

	public static boolean isDataEnd(byte[] data, int index, String boundary) {
		index = removeSuffixBlankLine(data, index);
		StringBuilder line = new StringBuilder();

		readLine(data, line, index);
		
		if (line.toString().indexOf(boundary) != -1) {
			return true;
		} else {
			return false;
		}
	}

}
